package com.compunit.composite.core.xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.compunit.composite.core.interfaces.xml.IXMLTag;

/**
 * <code>XMLTag</code> defines the implementation of
 * a single XML tag that contains an unique name, a
 * list of children and a set of key value pairs.
 *
 * @author Yi Wang (Neakor)
 * @version Creation date: 11-06-2009 19:04 EST
 * @version Modified date: 12-05-2009 14:28 EST
 */
public class XMLTag implements IXMLTag {
	/**
	 * The <code>String</code> name of this tag.
	 */
	private final String name;
	/**
	 * The <code>Map</code> of <code>String</code> key
	 * and <code>String</code> value attributes.
	 */
	private final Map<String, String> attributes;
	/**
	 * The <code>List</code> of <code>Attribute</code>.
	 */
	private final List<Attribute> attlist;
	/**
	 * The <code>List</code> of <code>IXMLTag</code>
	 * children.
	 */
	private final List<IXMLTag> children;
	/**
	 * The parent <code>IXMLTag</code>.
	 */
	private IXMLTag parent;

	/**
	 * Constructor of <code>XMLTag</code>.
	 * @param name The <code>String</code> name of this tag.
	 */
	public XMLTag(final String name) {
		this.name = name;
		this.attributes = new HashMap<String, String>();
		this.attlist = new ArrayList<Attribute>();
		this.children = new ArrayList<IXMLTag>();
	}

	/**
	 * Add the given tag as a child to this tag.
	 * @param tag The <code>IXMLTag</code> to be added.
	 */
	public void addChild(final XMLTag tag) {
		if(this.children.contains(tag) || tag == null) return;
		this.children.add(tag);
		tag.parent = this;
	}

	/**
	 * Add the given key-value attribute to this tag.
	 * @param key The <code>String</code> key.
	 * @param value The <code>String</code> value.
	 */
	public void addAttribute(final String key, final String value) {
		if(this.attributes.containsKey(key) || key == null || value == null) return;
		this.attributes.put(key, value);
		this.attlist.add(new Attribute(key, value));
	}

	@Override
	public Iterable<String> getKeys() {
		return this.attributes.keySet();
	}

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public IXMLTag getParent() {
		return this.parent;
	}

	@Override
	public Iterable<IXMLTag> getChildren() {
		return this.children;
	}

	@Override
	public String getValue(final String key) {
		return this.attributes.get(key);
	}

	@Override
	public String toString() {
		return this.name;
	}

	@Override
	public String toExternal() {
		final StringBuilder builder = new StringBuilder();
		// Indentation.
		IXMLTag p = this.parent;
		while(p != null) {
			builder.append("	");
			p = p.getParent();
		}
		// Begin tag.
		builder.append("<").append(this.name);
		// Append attributes.
		for(final Attribute att : this.attlist) {
			builder.append(" ");
			builder.append(att.key).append("=").append("\"").append(att.value).append("\"");
		}
		// Children.
		if(this.children.isEmpty()) builder.append(" />\n");
		else {
			builder.append(">\n");
			for(final IXMLTag child : this.children) {
				builder.append(child.toExternal());
			}
			// Indentation.
			p = this.parent;
			while(p != null) {
				builder.append("	");
				p = p.getParent();
			}
			builder.append("</").append(this.name).append(">\n");
		}
		return builder.toString();
	}
	
	/**
	 * <code>Attribute</code> defines the implementation
	 * of the internal data structure used to store the
	 * key-value pair of attribute in their added order.
	 *
	 * @author Yi Wang (Neakor)
	 * @version Creation date: 11-21-2009 18:47 EST
	 * @version Modified date: 11-21-2009 18:50 EST
	 */
	private final class Attribute {
		/**
		 * The <code>String</code> key.
		 */
		private final String key;
		/**
		 * The <code>String</code> value.
		 */
		private final String value;
		
		/**
		 * Constructor of <code>Attribute</code>.
		 * @param key The <code>String</code> key.
		 * @param value The <code>String</code> value.
		 */
		private Attribute(final String key, final String value) {
			this.key = key;
			this.value = value;
		}
	}
}
